package com.learning.thread.juc.lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ClassName: ConditionTest
 * Description: CustomQueue 自定义队列
 * <p>
 * 如果只用一个condition(取的condition只能唤醒取的, 放的condition只能唤醒放的)，所以用两个 condition，区分 2 种不同功能的线程。
 * <p>
 * Date: 2016/11/26 17:45
 *
 * @author SAM SHO
 * @version V1.0
 */
public class CustomQueue {

    final Lock lock = new ReentrantLock();

    /**
     * 两个Condition基于同一个Lock
     * "放"功能的线程的 Condition
     */
    final Condition notFull = lock.newCondition();
    /**
     * "取"功能的线程的 Condition
     */
    final Condition notEmpty = lock.newCondition();

    /**
     * 容器容量为100
     */
    final Object[] items = new Object[100];

    private int putptr, takeptr, count;

    /**
     * 往队列放数据
     *
     * @param x
     * @throws InterruptedException
     */
    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            // 如果“缓冲已满”，则等待；直到“缓冲”不是满的，才将x添加到缓冲中。
            while (count == items.length) {
                notFull.await();
            }
            //把 x 放入队列
            items[putptr] = x;
            //如果位置放到100了，从新从 0 开始放
            if (++putptr == items.length) {
                putptr = 0;
            }
            ++count;//放进去一个就 count++
            notEmpty.signal();//唤醒"取"的 Condition
        } finally {
            lock.unlock();
        }
    }

    /**
     * 从队列取数据
     *
     * @return
     * @throws InterruptedException
     */
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            // 如果“缓冲为空”，则等待；直到“缓冲”不为空，才将x从缓冲中取出。
            while (count == 0) {
                // "取"的等待
                notEmpty.await();
            }
            Object x = items[takeptr];
            //取值已到 100 从 0开始取
            if (++takeptr == items.length) {
                takeptr = 0;
            }
            --count;//取走一个 count--
            notFull.signal();//唤醒"放"的
            return x;
        } finally {
            lock.unlock();
        }
    }
}
